#pragma once

#ifndef _CORE_CONNECTION_SOCKET_HANDLER_H_
#define _CORE_CONNECTION_SOCKET_HANDLER_H_

#include "Poco\BasicEvent.h"
#include "Poco\Net\StreamSocket.h"
#include "Poco\Net\SocketStream.h"
#include "Poco\Net\SocketReactor.h"
#include "Poco\Net\SocketNotification.h"
#include "Poco\DOM\Document.h"
#include "Poco\SAX\SAXParser.h"
#include "Poco\SAX\InputSource.h"


namespace ExamSystem
{
    using Poco::BasicEvent;
    using Poco::AutoPtr;
    using Poco::Net::StreamSocket;
    using Poco::Net::SocketStream;
    using Poco::Net::SocketReactor;
    using Poco::XML::Document;
    using Poco::XML::SAXParser;
    using Poco::XML::InputSource;

    using Poco::Net::ReadableNotification;
    using Poco::Net::ShutdownNotification;
    using Poco::Net::WritableNotification;
    using Poco::Net::ErrorNotification;
    using Poco::Net::TimeoutNotification;

    /**************************************************************************\
     Component:     CConnectionSocketHandler
     Description:   
        This class provides an interface for socket operations, including:
        1. A method to send keep-alive package. (for server socket)
        2. When a new package comes, this handler will check if it is a keep-alive
           or a reply package. For keep-alive package, it will send reply package.
           For reply package, it will ignore the package and reset the keep-alive 
           flag. Otherwise, it will emit a signal.
        3. A method to send data
    \**************************************************************************/
    class CConnectionSocketHandler
    {
    public:
        CConnectionSocketHandler(StreamSocket& socket, SocketReactor& reactor);
        ~CConnectionSocketHandler(void);

    public: // interfaces
        // Send a keep-alive package to the client
        void SendKeepAlivePackage(void);
        
        // Check if the last keep-alive package has been replied
        bool IsKeepAlivePackageReplied(void) const;

        // send a XML stanza
        bool SendXMLStanza(Document &xmlStanza);

        // Send raw bytes 
        bool SendRawByte(const void *data, int length);

    public:
        // this event will be emit when the received package is not a keep-alive reply
        //BasicEvent<Document> eventNewPackageReached;

    protected:
        // event handlers
        void OnReadable(const AutoPtr<ReadableNotification>& pNf);
        void OnWritable(const AutoPtr<WritableNotification> &pNf);
        void OnShutdown(const AutoPtr<ShutdownNotification>& pNf);
        void OnError(const AutoPtr<ErrorNotification>& pNf);
        void OnTimeout(const AutoPtr<TimeoutNotification>& pNf);

        // check if received string is a complete package
        // return true when more checks are needed.
        bool CheckReceivedString(void);

        // process keep-alive package
        // send reply package to keep-alive package
        void OnKeepAlivePackage(void);

        // process keep-alive reply
        // set m_flagKeepAlivePackageReplied
        void OnKeepAliveReplyPackage(void);

    protected:
        // this flag identifies whether last keep-alive package has been replied
        bool m_flagKeepAlivePackageReplied;

        // this flag identifies whether the received label is the first label
        // According to the protocol, the <examination> label must be the first one.
        // The connection will ignore all bytes until it received a xml label.
        // If the first label is not <examination>, the connection is not valid and will
        // be closed immediately. 
        bool m_flagFirstLabelReceived;

        // receiving buffer
        enum { ReceivingBufferSize = 1024 };
        char m_receivingBuffer[ReceivingBufferSize];

        // received string
        // as we are using UTF-8 as transmission encoding, the string should be char type
        std::string m_receivedString;

        // managed socket and reactor
        StreamSocket m_socket;
        SocketReactor &m_reactor;
    };
}

#endif